home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / lisp / clue.lha / clue / doc / examples.text < prev    next >
Lisp/Scheme  |  1989-07-13  |  70KB  |  1,460 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.                    Programming With CLUE: An Extended Example
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.                                  Kerry Kimbrough
  14.  
  15.  
  16.  
  17.  
  18.                                    Version 6.0
  19.                                    July, 1989
  20.  
  21.  
  22.  
  23.  
  24.  
  25.                    Copyright (C) 1989 Texas Instruments Incorporated.                
  26.                                                                                      
  27.     Permission is granted to any individual or institution to use, copy, modify,
  28.     and distribute  this  document,  provided  that  this complete copyright and
  29.     permission notice is maintained, intact, in all copies.
  30.                                                                                      
  31.     Texas Instruments  Incorporated  provides  this  document  "as  is"  without
  32.     express or implied warranty.
  33.  
  34.  
  35.  
  36.  
  37.  
  38. This document contains  detailed examples  of CLUE  programming, all  based on a
  39. simple CLUE  program  and  the  set  of  contact  classes  used to implement it.
  40. Complete sources for these examples may be found in clue/examples/menu.l Some of
  41. the functions used are defined by CLX, the basic Common Lisp interface to the  X
  42. Window System.
  43.  
  44. The example "application" program presents a simple pop-up menu consisting of  a
  45. column of  strings:  a  title  string  followed  by  selectable menu items.  The
  46. application program itself relies upon the following two types of contacts.
  47.  
  48. button
  49.     A pointer-sensitive area containing a text label.  Moving the pointer cursor
  50.     onto a button causes it to be highlighted with a rectangular border.  If the
  51.     pointer cursor is  inside a  button, then  clicking any  pointer button will
  52.     cause the button contact to be  selected.  That is, the :select  callback of
  53.     the button is invoked.
  54.  
  55. menu
  56.     A  shell  contact  which  contains  a  column  of  buttons.   A  menu  shell
  57.     automatically fills in its content with a title-frame, which provides a menu
  58.     title string;  and  a  choices  composite,  which  arranges and controls the
  59.     selectable  button  items.   When  an   item  has  been  selected,   a  menu
  60.     automatically "pops down" and then invokes its own :select callback.
  61.  
  62.  
  63. In order to implement menu and button contacts, three other contact  classes are
  64. shown.
  65.  
  66. title-frame
  67.     A special composite which labels its single child with a title string.
  68.  
  69. choices
  70.     A collection of items for selection.  Each child of a choices composite is
  71.     regarded as a selectable item.  Therefore, a choices automatically defines a
  72.     :select callback function for each of its children.  This callback allows a
  73.     choices composite to report when a child has been selected and to record the
  74.     selected child for future reference.
  75.  
  76. column
  77.     A geometry manager composite which arranges its children in a single
  78.     vertical column and ensures that all children have the same size.  The
  79.     choices class is a subclass of column that uses column geometry management
  80.     to lay out selectable items.
  81.  
  82.  
  83. 1. Writing A CLUE Application
  84.  
  85. The complete  application  program  is  the  function just-say-lisp shown below.
  86. just-say-lisp has the  typical structure  of a  CLUE application,  consisting of
  87. four steps.
  88.         
  89.     1. Open a connection to an X display server and create a contact-display.
  90.  
  91.     2. Initialize the user interface by creating a set of contacts.
  92.  
  93.     3. Enter an event loop to process user input.
  94.  
  95.     4. Terminate by leaving the event loop, destroying all contacts and closing
  96.        the display server connection.
  97.  
  98.  
  99. (defun just-say-lisp (host &optional (font-name "fg-16"))
  100.   (let* ((display   (open-contact-display 'just-say-lisp :host host))
  101.          (screen    (contact-screen (display-root display)))
  102.          (fg-color  (screen-black-pixel screen))
  103.          (bg-color  (screen-white-pixel screen))
  104.  
  105.          ;; Create menu
  106.          (menu      (make-contact
  107.                       'menu
  108.                       :parent     display
  109.                       :font       font-name
  110.                       :title      "Please pick your favorite language:"
  111.                       :foreground fg-color
  112.                       :background bg-color))
  113.          (menu-mgr  (menu-manager menu)))    
  114.     
  115.     ;; Create menu items
  116.     (dolist (label '("Fortran" "APL" "Forth" "Lisp"))
  117.       (make-contact
  118.         'button
  119.         :parent     menu-mgr
  120.         :label      label
  121.         :foreground fg-color))
  122.        
  123.     ;; Bedevil the user until he picks a nice programming language
  124.     (unwind-protect
  125.         (loop
  126.           ;; Pop up menu at current pointer position
  127.           (multiple-value-bind (x y) (query-pointer (contact-parent menu))
  128.             (let ((choice (menu-choose menu x y)))
  129.               (when (string-equal "Lisp" choice)
  130.                 (return)))))      
  131.  
  132.       (close-display display))))
  133.  
  134.  
  135. 1.1. Creating A Contact Display
  136.  
  137. The open-contact-display function creates a  connection to the X  display server
  138. named by the :host argument.  A contact-display representing this  connection is
  139. returned.
  140.  
  141.   (let* ((display   (open-contact-display 'just-say-lisp :host host))
  142.          (screen    (contact-screen (display-root display)))
  143.          (fg-color  (screen-black-pixel screen))
  144.          (bg-color  (screen-white-pixel screen))
  145.  
  146. The only required argument to open-contact-display is a display name symbol (for
  147. example, just-say-lisp).   This  symbol  acts  as  a  name for a specific server
  148. connection.   However,  since  an  application  typically  uses  only  a  single
  149. contact-display, the display  name generally  serves as  a name  for the  entire
  150. application.  As an  application name,  a display  name is  used primarily  as a
  151. top-level component  in  resource  names.   See  Section 5.1 for examples of how
  152. resources are used.  A display name may also be used by a window manager client.
  153. For example, the name of a  contact-display is used to initialize  the :wm_class
  154. property of  all  its  shells;  some  window  managers  may  choose to show this
  155. property in window  title bars.   Some window  managers may  also allow users to
  156. specify application properties  as window  manager resources,  using the display
  157. name as a component in resource names.
  158.  
  159. Several functions  in  CLUE  and  CLX  operate  on a contact-display in order to
  160. access various attributes of the display server.  For example, the  display-root
  161. function returns a  root contact  corresponding to  a display  screen.  A screen
  162. object, in turn, may  be accessed to  determine screen-specific attributes  (for
  163. example, the  pixel  values  corresponding  to  black  and white in the screen's
  164. default colormap).
  165.  
  166. 1.2. Creating Contacts
  167.  
  168. The make-contact function is used to create an instance of a  particular contact
  169. class.
  170.          ;; Create menu
  171.          (menu      (make-contact
  172.                       'menu
  173.                       :parent     display
  174.                       :font       font-name
  175.                       :title      "Please pick your favorite language:"
  176.                       :foreground fg-color
  177.                       :background bg-color))
  178.  
  179. make-contact has two  required arguments  --- the  name of  contact class  (e.g.
  180. menu) and the parent of the new contact (given by the :parent keyword argument).
  181. For convenience,  a  contact-display  may  be  given  as  the  :parent;  this is
  182. equivalent to setting the :parent to (display-root display).
  183.  
  184. If the :parent is a  contact-display or a root,  then the new contact  will be a
  185. "top-level  contact."   All   top-level   contacts   should   be   instances  of
  186. override-shell, top-level-shell, top-level-session, or one of their  subclasses.
  187. (In the example  above, menu  is a  subclass of  override-shell.) These  classes
  188. implement the  special  behavior  required  of  top-level  X windows in order to
  189. interact properly  with  window  managers  and  session managers.  CLUE does not
  190. prevent programs from creating a  non-shell top-level contact, but  the behavior
  191. of such contacts may be unpredictable.
  192.  
  193. Other arguments  to  make-contact  are  initargs which initialize class-specific
  194. attributes.  The default values for initargs depend on the implementation of the
  195. contact class.  Typically, most initargs correspond to contact resources.   When
  196. resource initargs are omitted, then a default value may be read from a  resource
  197. database initialized by  an individual  user.  A  good practice  for application
  198. programmers is to minimize the number of initargs set in the program and thus to
  199. increase the ability  of a  user to  fine-tune the  user interface  with his own
  200. resource preferences.
  201.  
  202. Note that in the just-say-lisp example, the menu and button contacts are created
  203. without initializing their  position or  size.  The  reason is  that all contact
  204. geometry is computed  automatically.  The  size and  position of  each button is
  205. determined partly by the font  and string used for  its label and partly  by the
  206. geometry management policy provided by  its parent.  The button  parent returned
  207. by the menu-manager function is a choices composite created automatically by the
  208. menu shell.  Similarly, the  size of the  menu is determined  from the size  and
  209. layout of the buttons it contains.
  210.  
  211.  
  212.  
  213. 1.3. The Event Loop
  214.  
  215. In the main  body of  the just-say-lisp  function, the  menu-choose function  is
  216. called repeatedly.  menu-choose presents the menu at a specified location, waits
  217. for the user  to make  a selection,  then returns  the selected  item.  The  CLX
  218. function query-pointer is used to  determine the current pointer  position where
  219. the menu will appear.
  220.  
  221.         (loop
  222.           ;; Pop up menu at current pointer position
  223.           (multiple-value-bind (x y) (query-pointer (contact-parent menu))
  224.             (let ((choice (menu-choose menu x y)))
  225.               (when (string-equal "Lisp" choice)
  226.                 (return))))) 
  227.  
  228. menu-choose, which contains the program event  loop, is shown below.  Note  that
  229. the event  loop  is  terminated  when  an  item  is  selected  by a throw to the
  230. :menu-selection  tag.   add-callback  is  used  to  program  the  menu's :select
  231. callback to invoke the throw.  This is done by defining the throw-menu-selection
  232. function as the :select callback function.
  233.  
  234.   (defun menu-choose (menu x y)
  235.     "Present the MENU at the given location and return the label of the
  236.   item chosen. If no item is chosen, then nil is returned."
  237.   
  238.     ;; Set menu callback to return chosen item label
  239.     (add-callback menu :select 'throw-menu-selection menu)
  240.   
  241.     ;; Display the menu so that first item is at x,y.
  242.     (menu-present menu x y)
  243.   
  244.     ;; Event processing loop
  245.     (catch :menu-selection
  246.       (loop (process-next-event (contact-display menu)))))
  247.   
  248.   
  249.   (defun throw-menu-selection (menu)
  250.     "Throw to :menu-selection tag, returning the label of the selected menu button (if any)."
  251.     (let ((selection (choice-selection (menu-manager menu))))
  252.       (throw :menu-selection
  253.              (when selection (button-label selection)))))
  254.   
  255.  
  256. Note that  another  program  could  change  many  aspects of the menu's behavior
  257. simply by defining a different :select  callback function.  For example, a  menu
  258. selection might cause another function to be executed without exiting the  event
  259. loop.  Also, the value corresponding to  a selected menu item could  be computed
  260. differently, rather than simply returning the item button label string.
  261.  
  262.  
  263. 1.4 Managing Contacts
  264.  
  265. menu-choose calls the function  menu-present in order  to display the  menu at a
  266. given location.
  267.  
  268.   (defun menu-present (menu x y)
  269.     "Present the MENU with the first item centered on the given position."
  270.     ;; Complete initial geometry management before positioning menu
  271.     (unless (realized-p menu)
  272.       (initialize-geometry menu))
  273.   
  274.     (let ((parent  (contact-parent menu))
  275.           (item    (first (composite-children (menu-manager menu)))))
  276.       
  277.       ;; Compute the y position of the center of the first item
  278.       ;; with respect to the menu
  279.       (multiple-value-bind (item-x item-y)
  280.           (contact-translate item 0 (round (contact-height item) 2) menu)
  281.         (declare (ignore item-x))
  282.   
  283.         ;; Try to center first item at the given location, but
  284.         ;; make sure menu is completely visible in its parent  
  285.         (change-geometry
  286.           menu
  287.           :x (max 0 (min (- (contact-width parent) (contact-width menu))
  288.                          (- x (round (contact-width menu) 2))))
  289.           :y (max 0 (min (- (contact-height parent) (contact-height menu))
  290.                          (- y item-y)))
  291.           :accept-p t)))
  292.     
  293.     ;; Make menu visible
  294.     (setf (contact-state menu) :mapped))
  295.  
  296.  
  297. menu-present demonstrates several CLUE features used by application  programmers
  298. to manage contacts.   The (setf  contact-state) function  is used  to manage the
  299. menu's visual state.  The functions initialize-geometry and change-geometry  are
  300. used to manage menu geometry.
  301.  
  302. (setf contact-state) is the function that changes the visual state of the  menu.
  303. Setting the state  to :mapped  causes the  menu to  be displayed  at its current
  304. position.  Actually, mapping the menu causes  the X server to send  an :exposure
  305. event for  the  menu.   Then,  inside  the  process-next-event  loop,  when CLUE
  306. receives this event, the menu display method is called automatically.
  307.  
  308. change-geometry is called to move the  menu to the current pointer  position ---
  309. that is, to change the x  and y position of the  upper left corner of the  menu.
  310. change-geometry actually requests approval for a geometry change from the parent
  311. of the  menu.   The  actual  effect  of  change-geometry depends on the parent's
  312. geometry management policy.  Later examples describe geometry management in more
  313. detail.  In  this  case,  the  :accept-p  argument  to  change-geometry is true,
  314. indicating that the new position determined by the parent's geometry  management
  315. will be used.
  316.  
  317. initialize-geometry is  a  special  function  that  is  not  used  by  most CLUE
  318. programs.  By invoking the  process of geometry  management, initialize-geometry
  319. causes the  initial  geometry  for  the  menu  and  all of its descendants to be
  320. computed before they are actually realized.  Ordinarily, initialize-geometry  is
  321. called automatically just before a composite is realized; realizing the  contact
  322. is also a step that is usually invoked automatically just before the event  loop
  323. begins.  But the menu is a special case.  Why?
  324.  
  325. Note that  in  order  for  menu-present  to  pop  up  the  menu over the pointer
  326. position, the initial size  of the menu  must be known  before the menu  becomes
  327. :mapped.  Recall that the final  menu size depends on  the number of items,  the
  328. size of the  item labels,  etc.  and  that computing  the initial  size involves
  329. geometry management.  For most contacts, this initial geometry management can be
  330. performed automatically simply by ensuring  that the contact is  "managed", i.e.
  331. neither :mapped nor  :withdrawn.  But,  because the  menu is  an instance  of an
  332. override-shell, it cannot be managed until it :mapped.  What is needed is a  way
  333. to compute the initial  geometry management for  the menu before  it is managed.
  334. That is the special purpose of initialize-geometry.  Note that after the menu is
  335. realized, initialize-geometry is no longer necesssary.
  336.  
  337.  
  338. 1.5. Terminating the Program
  339.  
  340. The just-say-lisp program ends when the user selects the "right" item.  The  CLX
  341. close-display function is called to close the connection to the X server.   This
  342. also causes the menu and all other server resources created for this display  to
  343. be destroyed.
  344.  
  345.     (unwind-protect
  346.         (loop
  347.           ...
  348.           )      
  349.  
  350.       (close-display display))
  351.  
  352. A good programming practice illustrated by  just-say-lisp is to place the  event
  353. loop inside  an  unwind-protect  form  and  to  include  close-display among the
  354. cleanup forms.  Without  this protection,  an unexpected  error could  cause the
  355. program to terminate without  freeing the server  resources it has  created.  If
  356. server resources  are  repeatedly  created  without  being destroyed, then the X
  357. server will eventually run out of memory and fail.
  358.  
  359.  
  360. 2. Implementing A Menu
  361.  
  362. This section  looks  at  the  CLUE  features  used  by  a  contact programmer to
  363. implement the menu contact class.
  364.  
  365. 2.1 Defining a Contact Class
  366.  
  367. The menu class is defined by a defcontact form which specifies its superclasses,
  368. its slots, and its resources.
  369.  
  370.     (defcontact menu (override-shell)           ; A subclass of override-shell with...
  371.       ()                                        ; ...no additional slots, and...
  372.       (:resources
  373.         (font       :type     font)             ; ... class resources, and...
  374.         (foreground :type     pixel)
  375.         (title      :type     string)
  376.            
  377.         (state      :initform :withdrawn))      ; ...a different default state
  378.       
  379.       (:documentation
  380.         "Presents a column of menu items."))
  381.  
  382. The syntax of defcontact  is very similar  to that of  defclass.  The difference
  383. lies in the resource specifications which  are special to CLUE contact  classes.
  384. Resource specifications declare contact initargs that can be initialized from  a
  385. resource database.  For example, the resource specifications for menu mean  that
  386. a :font initarg can be given to  make-contact when creating a font and  that the
  387. default value for :font can found in the resource database.  The :type given for
  388. the menu  font  resource  means  that  the  :font  value  will  automatically be
  389. converted to the 'xlib:font type.  Resource type conversion involves the convert
  390. function, which is discussed later.
  391.  
  392. A subclass also inherits resource specifications from its superclasses.  In this
  393. case, the menu class  inherits the state  resource from the  basic contact class
  394. but assigns it a different default value of :withdrawn .  In other words, a menu
  395. will not automatically become  :mapped by default  like most contacts  (instead,
  396. this is done later by  menu-present after the menu  has been moved to  the right
  397. position).  Note that to modify an inherited resource, it is sufficient to  list
  398. only the changed part of its resource specification.
  399.  
  400. 2.2. Class Initialization
  401.  
  402. The initialize-instance function  is part  of the  standard CLOS  initialization
  403. protocol invoked by make-contact.  A contact programmer will typically define an
  404. :after method for initialize-instance in  order to implement any  class-specific
  405. initializations.  The  :after  method  for  the  menu  class creates most of the
  406. pieces used  to  construct  a  menu  automatically.   As  a  result, application
  407. programs do not need to be aware of the details of the menu contact hierarchy.
  408.  
  409.     (defmethod initialize-instance :after
  410.       ((menu menu) &key title font foreground background &allow-other-keys)
  411.       
  412.       ;; Create title-frame containing choices child to manage menu items
  413.       (let* ((title   (make-contact
  414.                         'title-frame
  415.                         :parent     menu
  416.                         :name       :title
  417.                         :text       title
  418.                         :font       font
  419.                         :foreground foreground
  420.                         :background (or background :white)))
  421.              
  422.              (manager (make-contact
  423.                         'choices
  424.                         :parent       title
  425.                         :name         :manager
  426.                         :border-width 0)))
  427.     
  428.         ;; Define callback to handle effect of selection
  429.         (add-callback manager :select 'popup-menu-select menu)
  430.     
  431.         ;; Moving pointer off menu causes nil selection
  432.         (add-event manager
  433.                    '(:leave-notify :ancestor :nonlinear)
  434.                    '(choice-select nil))))
  435.     
  436.     
  437.     
  438. When a menu is returned from make-contact, it contains a title-frame showing the
  439. menu title.  In turn, the title-frame contains a choices composite known as  the
  440. "menu manager".  Menu  items are  then created  by adding  children to this menu
  441. manager.   This   hierarchy   structure   exploits   the   power   of  CLOS  and
  442. object-oriented programming  to  provide  extra  implementation modularity.  The
  443. functions for title  display, item  arrangement, and  overall menu  behavior are
  444. handled by separate classes.  As a result, new types of menus to be  implemented
  445. easily by substituting new classes for each of these functions.
  446.  
  447. Two other aspects  of the  menu are  also initialized  here upon  creation.  The
  448. menu-manager, a choices composite,  uses the :select  callback to signal  when a
  449. user has selected  an item.   Because the  menu class  implements a pop-up menu,
  450. add-callback is  called  to  initialize  this  callback  with  a  function  that
  451. implements the  "pop  down"  side  effect  of  selecting an item.  When the menu
  452. manager's :select callback is invoked, the popup-menu-select function  withdraws
  453. the menu before invoking  the application function  associated with the  :select
  454. callback of the menu.
  455.  
  456.     (defmethod popup-menu-select ((menu menu))
  457.       ;; Pop down immediately
  458.       (setf (contact-state menu) :withdrawn)
  459.       (display-force-output (contact-display menu))
  460.     
  461.       ;; Invoke menu callback
  462.       (apply-callback menu :select))
  463.     
  464. In addition, add-event is  used to define  an event translation  that implements
  465. another aspect  of  pop  menu  behavior.   The  details  of  defining this event
  466. translation are shown in the next section.
  467.  
  468.  
  469.  
  470. 2.3. Defining Event Translations
  471.  
  472.  
  473. The following event translation, which is  attached to the menu manager,  causes
  474. the manager's choice-select action  to performed whenever  the pointer is  moved
  475. outside the menu manager.
  476.  
  477.         ;; Moving pointer off menu causes nil selection
  478.         (add-event manager
  479.                    '(:leave-notify :ancestor :nonlinear)
  480.                    '(choice-select nil))
  481.    
  482.  
  483. The argument  passed  to  the  choice-select  function  is  nil.   The item thus
  484. selected is nil; that is, moving the  pointer off the menu to disappear  without
  485. actually selecting an item (see Section 4.2, The Menu Manager, for a description
  486. of of  choice-select).   Note  that  this  is an instance event translation that
  487. applies only to  a specific  menu manager  instance.  An  alternative might have
  488. been to use defevent to define  this event translation for all  instances of the
  489. choices class.  However, this would assign "select nil when leaving" behavior to
  490. all choices  contacts;  using  an  instance  translation instead allows the same
  491. choices class to be used in cases where this behavior is is not desirable.
  492.  
  493. Like any X window, a  contact must select the  types of events that  it wants to
  494. receive and process.  The event-mask slot contains a bit string representing the
  495. events selected for  a contact.   But CLUE  programs rarely  need to  access the
  496. event-mask directly.  For example, this call to add-event automatically  updates
  497. the menu manager's event-mask to select :leave-notify events.
  498.  
  499. The event specification  syntax used  here is  an extension  to the  basic event
  500. specifications provided by  CLUE.  The  extension defines  a new  type of  event
  501. specification of the form  (:leave-notify kind*), where  each kind is  a keyword
  502. identifying a kind  of :leave-notify  event.  This  type of  event specification
  503. will match a :leave-notify event if the event is one of the specified kinds.   A
  504. contact programmer creates this  new type of  event specification by  defining a
  505. check function and a match functon.
  506.  
  507.     (defun leave-check (event-key &rest kinds)
  508.       (dolist (kind kinds)
  509.         (unless (member kind '(:ancestor :virtual :inferior :nonlinear :nonlinear-virtual))
  510.           (error "~s isn't a valid kind of ~s event" kind event-key)))
  511.       (list 'leave-match kinds))
  512.     
  513.     (defun leave-match (event kinds)
  514.       (member (slot-value event 'kind) kinds :test #'eq))
  515.     
  516.     (setf (check-function :leave-notify) 'leave-check)
  517.  
  518. The check-function set  for the  :leave-notify event  type is  leave-check.  The
  519. arguments to leave-check are the elements of the event-specification list --- an
  520. event type keyword and a set of event kind keywords.  After verifying that  only
  521. valid kinds are specified, leave-check returns a list containing the leave-match
  522. function and the "canonical form" of the event specification.  Inside the  event
  523. loop, leave-match will be called to  compare an input event with  this canonical
  524. form.  leave-match reports a  match when the  event is a  :leave-notify event of
  525. one of the kinds specified by the canonical event specification.
  526.  
  527.  
  528. 3. Implementing A Button
  529.  
  530. This section  looks  at  the  CLUE  features  used  by  a  contact programmer to
  531. implement the button contact class.
  532.  
  533.  
  534. 3.1 Slot Resources
  535.  
  536. A button is  a non-composite  contact with  a set  of attributes  represented by
  537. slots --- a  text label  plus a  font and  a foreground  color used  to draw the
  538. label.  All  of  these  attribute  slots  are  also declared as resources.  As a
  539. result, when an  instance of  a button  is created,  CLUE automatically converts
  540. values given  for  these  resource  slots  to the specified representation type.
  541. This applies  not  only  to  initarg  values  given  to make-contact but also to
  542. default values found  in the  resource database  or in  resource initforms.  For
  543. example, the default initform for the font slot is the font name "fg-16"  which,
  544. if used, is automatically converted to a font object via the open-font function.
  545. Similarly, the default foreground value of :black is automatically converted  to
  546. an appropriate pixel value using  screen-black-pixel.  CLUE defines a  number of
  547. built-in convert methods  that can  convert among  commonly-used representations
  548. for X objects like fonts, pixels, pixmaps, and images.  Programmers are free  to
  549. extend this mechanism by defining their own convert methods.
  550.  
  551.     (defcontact button (contact)
  552.       
  553.       ((label
  554.          :accessor   button-label
  555.          :initarg    :label
  556.          :initform   ""
  557.          :type       string)
  558.     
  559.        (font
  560.          :accessor   button-font
  561.          :initarg    :font
  562.          :initform   "fg-16"
  563.          :type       font)
  564.     
  565.        (foreground
  566.          :accessor   button-foreground
  567.          :initarg    :foreground
  568.          :initform   :black
  569.          :type       pixel)
  570.     
  571.        (compress-exposures
  572.          :allocation :class
  573.          :initform   :on
  574.          :reader     contact-compress-exposures
  575.          :type       (member :off :on)))
  576.       
  577.       (:resources
  578.         (background :initform :white)
  579.         (border     :initform :white)
  580.         font
  581.         foreground
  582.         label)
  583.       
  584.       (:documentation
  585.         "Triggers an action."))
  586.  
  587.  
  588. Notice also that several slots inherited  from the base contact class  are given
  589. new default values for  the button class.   The background and  border resources
  590. for buttons default  to :white.   The compress-exposures  slot defaults  to :on.
  591. Thus, :exposure events for buttons are "compressed" --- all but the final member
  592. in  a  sequence  of  button  :exposure  events  are  discarded.   Why?   Because
  593. displaying a button contact is a simple matter of drawing its text label, it  is
  594. more efficient  to  draw  the  whole  string  at  once  than  it is to draw each
  595. individual exposed piece.  event-compress is  a class slot and  therefore cannot
  596. be a resource;  its new  default value  for the  button class  must therefore be
  597. defined in a slot specification instead of a resource specification.
  598.  
  599. Of course, class resources need not  be represented as slots.  For  example, the
  600. defcontact form for the menu class shown earlier declares non-slot resources for
  601. font, foreground  color,  and  title  string.   Non-slot  resources  values  are
  602. contained in the  argument list  to initialize-instance.   As shown  earlier for
  603. menu,  non-slot   resources   are   typically   used   in   :after  methods  for
  604. initialize-instance to initialize other components of a contact instance.
  605.  
  606.  
  607. 3.2 Displaying a Contact
  608.     
  609. Every class of contacts that contains something to display must define a  method
  610. for the display function.   CLUE automatically invokes  this method whenever  an
  611. :exposure event for the contact is processed.  Some contact classes do not  need
  612. a display method.  For example, by default, composite subclasses do not  receive
  613. :exposure events because all displayable  information is typically contained  in
  614. the children.
  615.  
  616. The display  method  for  the  button  class  is shown below.  Because :exposure
  617. events are  compressed,  this  method  displays  the  entire button contents and
  618. ignores the x, y, width, and height arguments which define the region exposed.
  619.     
  620.     (defmethod display ((button button) &optional x y width height &key)
  621.       (declare (ignore x y width height))
  622.       
  623.       (with-slots
  624.         (font label foreground (button-width width) (button-height height))
  625.         button
  626.         
  627.         ;; Get metrics for label string
  628.         (multiple-value-bind (label-width ascent descent left right font-ascent font-descent)
  629.             (text-extents font label)
  630.           (declare (ignore ascent descent left right))
  631.     
  632.           ;; Center label in button
  633.           (let ((label-x (round (- button-width label-width) 2))
  634.                 (label-y (+ (round (- button-height font-ascent font-descent) 2)
  635.                             font-ascent)))
  636.     
  637.             ;; Use an appropriate graphics context from the cache
  638.             (using-gcontext (gc :drawable   button
  639.                                 :font       font
  640.                                 :foreground foreground)
  641.               (draw-glyphs button gc label-x label-y label))))))
  642.  
  643. Note that using-gcontext  is used  to find  a graphics  context for  drawing the
  644. label  characters.   using-gcontext  searches  a  cache  of   previously-created
  645. gcontext objects, locates one with the given font and foreground attributes, and
  646. binds it to the symbol gc for use with the draw-glyphs function .  The advantage
  647. of using-gcontext is that  the total number  of gcontext objects  created by the
  648. program can be minimized.   Saving a gcontext  with each button  instance is not
  649. required, and slots are  allocated only for  the individual gcontext  components
  650. used.  The disadvantage of using-gcontext  is that searching the  gcontext cache
  651. makes displaying somewhat slower.
  652.     
  653.  
  654. 3.3 Defining a Preferred Size
  655.  
  656. A button is not a  composite and therefore does  not act as a  geometry manager.
  657. However, a  non-composite  class  can  play  a  part  in  geometry management by
  658. defining a preferred size.  Some geometry managers will call the  preferred-size
  659. function to ask for a child's advice about its best size.  Therefore, defining a
  660. preferred-size  method  for  each  contact  class  is  a  good  practice.    The
  661. preferred-size method for the button class is shown below.
  662.  
  663.     (defmethod preferred-size ((button button) &key new-width new-height new-border-width)
  664.       (with-slots (font label border-width) button
  665.         
  666.         ;; Get metrics for label string
  667.         (multiple-value-bind (label-width ascent descent left right font-ascent font-descent)
  668.             (text-extents font label)
  669.           (declare (ignore ascent descent left right))
  670.     
  671.           (let* ((margin      2)
  672.                  (best-width  (+ label-width margin margin))
  673.                  (best-height (+  font-ascent font-descent margin margin)))
  674.     
  675.             ;; Return best geometry for this label
  676.             (values
  677.               (if new-width  (max new-width best-width)   best-width)
  678.               (if new-height (max new-height best-height) best-height)
  679.               (or new-border-width border-width))))))
  680.  
  681. For a button,  the preferred  size returned  is one  big enough  to contain  the
  682. entire label string,  including a  margin of  two pixels  around all sides.  The
  683. :new-width and :new-height  arguments give  the size  suggested by  the geometry
  684. manager; if it is big enough, then  this suggested size is returned.  Note  that
  685. the border width of the button is unimportant; this method simply returns either
  686. the suggested value or the current value for border width.
  687.  
  688. 3.4 Handling Contact Input
  689.  
  690. A button is  a simple  contact that  responds to  user input  in just  two ways.
  691. Moving the pointer  cursor onto  a button  causes it  to be  highlighted with  a
  692. rectangular border.  If the pointer cursor is inside a button, then clicking any
  693. pointer button will cause  the button contact  to be selected.   These responses
  694. are  implemented   by   two   action   functions   ---  button-set-highlight and
  695. button-select.
  696.  
  697. button-set-highlight highlights  or  unhighlights  the  button, depending on the
  698. on-p argument.  If on-p is true, then  the button is highlighted by drawing  its
  699. border in  the  foreground  color;  otherwise,  the  button  is unhighlighted by
  700. drawing its border in the background color.
  701.     
  702.     (defmethod button-set-highlight ((button button) on-p)
  703.       (with-slots (foreground background) button
  704.         (setf (window-border button) (if on-p foreground background))))
  705.  
  706. button-select simply invokes the :select callback of the button.  The effect  of
  707. this depends on the callback function actually associated with :select.
  708.     
  709.     (defmethod button-select ((button button))
  710.       (apply-callback button :select))
  711.  
  712. Notice that the responses performed by these actions are not necessarily related
  713. to any specific type of input event.  For each action, an event translation must
  714. be established that  will connect  the action  with the  event that triggers it.
  715. The button  class  uses  the  following  class  event translations to make these
  716. connections.  Like add-event, defevent causes  the event-mask of each  button to
  717. select :button-press, :enter-notify, and :leave-notify event.
  718.     
  719.     (defevent button :button-press button-select)
  720.     (defevent button :enter-notify (button-set-highlight t))
  721.     (defevent button :leave-notify (button-set-highlight nil))
  722.     
  723. These class event translations  represent defaults that  might be overridden  by
  724. more specific instance translations.   For example, the  event-translations slot
  725. of a contact is declared as a resource.  This means that by defining a value for
  726. event-translations in the  resource database,  a user  can initialize  a contact
  727. with new  instance  translations  that  customize  the connections between event
  728. types and actions.
  729.     
  730.  
  731.  
  732. 4. Implementing the Menu Hierarchy
  733.  
  734. A menu consists of a hierarchy of contacts, each of which implements a  specific
  735. part of a menu's behavior.  A contact class is defined for each part of the menu
  736. hierarchy.
  737.  
  738. menu            A shell which acts as the the top-level container for the menu.
  739.                 Provides the application program interface for overall menu
  740.                 behavior. A menu has a title-frame as its only child.
  741.  
  742. title-frame     Implements the layout and display of the menu title. A
  743.                 title-frame contains a choices contact as its only child.
  744.  
  745. choices         The manager for menu items. Allows the application program
  746.                 to define menu items. Coordinates the selection of menu items.
  747.  
  748. column          Implements geometry management for a column of menu items.
  749.  
  750. Most parts of  the menu  hierarchy are  created automatically  when the  menu is
  751. created (see Section 2.2, Class Initialization).  This section shows some of the
  752. CLUE features used to implement these classes.
  753.  
  754.  
  755. 4.1 The Menu Title
  756.  
  757. A title-frame has slots that specify title text, font, and foreground color. 
  758.  
  759.     (defcontact title-frame (composite)
  760.     
  761.       ((font
  762.          :accessor title-font
  763.          :initarg  :font
  764.          :initform "fg-16"
  765.          :type     font)
  766.     
  767.        (foreground
  768.          :accessor title-foreground
  769.          :initarg  :foreground
  770.          :initform :black
  771.          :type     pixel)   
  772.        
  773.        (text
  774.          :accessor title-text
  775.          :initarg  :text
  776.          :type     string)
  777.     
  778.        (compress-exposures
  779.          :allocation :class
  780.          :initform   :on
  781.          :reader     contact-compress-exposures
  782.          :type       (member :off :on)))
  783.       
  784.       (:resources
  785.         font
  786.         foreground
  787.         text
  788.         (event-mask :initform #.(make-event-mask :exposure)))
  789.       
  790.       (:documentation
  791.         "A composite consisting of a text title and another contact."))
  792.  
  793.  
  794. Unlike many composites, a title-frame  contains displayable information ---  the
  795. title string.  This means  that a title-frame  must select :exposure  events and
  796. must define a display method.  Defining  a new default value for  the event-mask
  797. resource  accomplishes  the  necessary  modification  to  the  composite   class
  798. defaults.  Note the use  of the CLX  function make-event-mask, which  converts a
  799. sequence of event mask keywords  into a bit string.   The same thing might  have
  800. been accomplished  by  using  defevent  to  define  a  title-frame  class  event
  801. translation for :exposure events.  However, this approach is inefficient because
  802. CLUE does not need such an event translation to handle :exposure events.
  803.  
  804.  
  805. The following title-frame  methods allow  a title-frame  to change  its size and
  806. title layout when necesssary.  For example, changing the title font to a  larger
  807. size should cause the content of  the title-frame (i.e.  the choices  containing
  808. menu items) to be moved slightly so that it does not cover up the title.
  809.  
  810.     (defmethod (setf title-font) (new-value (title-frame title-frame))
  811.       (title-update title-frame :font (convert title-frame new-value 'font)))
  812.     
  813.     (defmethod (setf title-text) (new-value (title-frame title-frame))
  814.       (title-update title-frame :text new-value))
  815.     
  816.     (defmethod title-update ((title-frame title-frame) &key text font)
  817.       (with-slots ((current-text text) (current-font font)) title-frame
  818.                 
  819.         ;; Update slots
  820.         (setf current-text (or text current-text)
  821.               current-font (or font current-font))
  822.         
  823.         ;; Update geometry
  824.         (when (realized-p title-frame)
  825.           (change-layout title-frame))))
  826.  
  827. Note that this method for (setf title-font) replaces the default method  created
  828. by the font slot :accessor option.   (setf title-frame) also calls convert  with
  829. the new font value  to handle conversion  to the necessary  representation type.
  830. This allows a program to  specify the new font  either as a font  name string or
  831. with a previously-opened font object.
  832.     
  833. title-update calls change-layout  to rearrange  the geometry  of the title-frame
  834. and its  content.   change-layout  is  a  geometry  management  function that is
  835. discussed later in more detail.
  836.  
  837.  
  838. 4.2  The Menu Manager
  839.  
  840. The choices class implements the functions of a menu manager:
  841.  
  842.         o   Serve as the parent for menu item contacts.
  843.  
  844.         o   Provide geometry management for menu items. The choices geometry
  845.             management policy is inherited from its column superclass, which is
  846.             described later.
  847.  
  848.         o   Coordinate menu item selection. 
  849.  
  850.         o   Record the currently-selected item.
  851.  
  852. The  choices   class   defines   a   single   selection   slot  to  contain  the
  853. currently-selected item.  Note that only a  :reader is specified for this  slot;
  854. selection should be done interactively by the user, not by the program.
  855.  
  856.     (defcontact choices (column)
  857.     
  858.       ((selection
  859.          :reader   choice-selection
  860.          :initform nil
  861.          :type     (or null contact)))
  862.       
  863.       (:documentation
  864.         "A column of items to choose from."))
  865.     
  866. Whenever a  contact  is  created,  the  add-child  function is called to add the
  867. contact to  its  parent's  set  of  children.   The primary method for add-child
  868. should not be modified,  but contact programmers  may define :after  methods for
  869. add-child that perform  child initializations  required for  a specific  contact
  870. class.   For  example,  the  add-child  :after  method  for  the  choices  class
  871. initializes the :select callback for each menu item.
  872.  
  873.     (defmethod add-child :after ((choices choices) child &key)
  874.       ;; Initialize child's :select callback
  875.       (add-callback child :select 'choice-select choices child))
  876.     
  877.     
  878.     (defmethod choice-select ((choices choices) child)
  879.       ;; Record current selection
  880.       (with-slots (selection) choices
  881.         (setf selection child))
  882.     
  883.       ;; Invoke selection callback
  884.       (apply-callback choices :select))
  885.  
  886. When initialized as  shown here,  the :select  callback for  each menu item will
  887. call the choice-select  function with  the selected  item child  as an argument.
  888. choice-select implements  the  choices  action  for  selecting  a  menu item; it
  889. records the current selection and invokes  the :select callback for the  choices
  890. menu manager.
  891.  
  892. 4.3 Selection Callbacks
  893.  
  894. Menu selection relies upon :select callbacks  from three different parts of  the
  895. menu hierarchy.
  896.  
  897.     Menu item       A menu item contact, such as a button, invokes its :select
  898.                     callback in response to user input. The associated function
  899.                     (choice-select) lets the menu manager know which item has
  900.                     been selected.
  901.  
  902.     Menu manager    The choices menu manager allows only a single item to be
  903.                     selected.  A choices contact invokes its :select callback
  904.                     when an item is selected.  The associated function
  905.                     (popup-menu-select) lets the menu know that it is time to
  906.                     withdraw the pop-up menu.
  907.  
  908.     Menu            A menu invokes its :select callback when menu has been
  909.                     withdrawn.  The associated function is defined by the
  910.                     application program.  In menu-choose, the associated
  911.                     function is throw-menu-selection, which terminates the event
  912.                     loop and returns the label of the selected item.
  913.  
  914. This distribution of  selection control  means that  knowledge about  the entire
  915. structure of the menu hierarchy  does not have to  be built into the  methods of
  916. classes that  implement  menu  items  and  menu  managers.  It also provides the
  917. flexibility to  implement  different  selection  policies.   For example, a menu
  918. manager that allowed  more than  one item  to be  selected could  be substituted
  919. easily.
  920.  
  921. 4.4  Menu Geometry Management
  922.  
  923. choices is  a  subclass  of  the  column  class,  which  implements its geometry
  924. management policy.
  925.  
  926.     (defcontact column (composite) ()
  927.       (:documentation
  928.         "Arranges its children in a vertical column."))
  929.  
  930. With no  slots  or  display  content  of  its  own,  column  is  a pure geometry
  931. management class.  That is,  its purpose is  simply to provide  methods for CLUE
  932. geometry management  functions.   Here  is  what  the column geometry management
  933. policy does:
  934.  
  935.         o   Item size
  936.                 All items are made to have the same size, namely the preferred
  937.                 size of the largest item. 
  938.  
  939.         o   Item layout
  940.                 Items are arranged in a vertical column and separated by a bit
  941.                 of space. They are also positioned horizontally so that they are
  942.                 centered in the column, and their inside left and right edges are
  943.                 aligned. 
  944.  
  945.         o   Handling item geometry
  946.                 If the biggest item gets bigger, then all items are given the
  947.                 new size.  An item can request a change to its border width;
  948.                 this is allowed and it causes the item to move so that its
  949.                 inside edges remained aligned with other items.  Item position
  950.                 is completely determined by the order of the children list and
  951.                 the column policy; an item is not permitted to move itself
  952.                 directly.
  953.  
  954.         o   Handling menu geometry
  955.                 In trying to arrange its items, a column will compute the size
  956.                 needed for itself --- that is, a size big enough to contain all
  957.                 items with a nice spacing. The column must request this size from
  958.                 its geometry manager, then somehow deal with whatever approved
  959.                 size it is given. 
  960.  
  961. In the case of a  menu, the geometry manager  (i.e.  the parent) of  the choices
  962. column is a title-frame.  This title-frame,  in turn, is managed by  its parent,
  963. the menu shell.  When the choices column requests its size be changed (based  on
  964. its policy  and  its  list  of  item  children),  then  its title-frame and menu
  965. ancestors responds similarly.   For both  a title-frame  and menu,  the geometry
  966. management policy is to be big enough to contain its content (the implementation
  967. of this policy is not shown here).  As a result, the requested column size  gets
  968. propagated back up  the menu  hierarchy until  every component  finds its  right
  969. size.  In  this  way,  the  sizes  for  the  top-level  menu  and  all the other
  970. intermediate contacts are computed automatically and do not need to be  assigned
  971. by the application programmer.
  972.  
  973. The following  examples  show  how  the  geometry  management methods for column
  974. implement this  policy.   The  method  for  change-layout  defines the effect of
  975. adding or  deleting  a  new  column  item.   Also,  when  a  column  is created,
  976. change-layout is called once to compute  the initial item layout.  In  CLUE, the
  977. change-layout method  of  a  composite  is  called  whenever  its set of managed
  978. children changes.
  979.  
  980.     (defmethod change-layout ((column column) &optional newly-managed)
  981.       (declare (ignore newly-managed))
  982.       (with-slots (width height) column
  983.     
  984.         ;; Compute the maximum preferred size of all children.
  985.         (multiple-value-bind (item-width item-height)
  986.             (column-item-size column)
  987.     
  988.           ;; Compute preferred column size, assuming this item size
  989.           (multiple-value-bind (preferred-width preferred-height)
  990.               (column-preferred-size column item-width item-height)
  991.             
  992.             ;; Try to ensure at least preferred size
  993.             (if
  994.               (or (setf preferred-width  (when (< width preferred-width)   preferred-width))
  995.                   (setf preferred-height (when (< height preferred-height) preferred-height)))
  996.               
  997.               ;; Ask parent for larger size
  998.               (change-geometry column
  999.                                :width    preferred-width
  1000.                                :height   preferred-height
  1001.                                :accept-p t)
  1002.               
  1003.               ;; Else current size is big enough
  1004.               (column-adjust column item-width item-height))))))
  1005.  
  1006.  
  1007. First, the preferred column size is computed, based on the current set of items.
  1008. This is done using the functions column-item-size and column-preferred-size.
  1009.  
  1010.  
  1011.     (defun column-item-size (column)
  1012.       "Return the maximum preferred width and height of all COLUMN children."
  1013.       (with-slots (children) column
  1014.         (let ((item-width 0) (item-height 0))
  1015.           (dolist (child children)
  1016.             (multiple-value-bind (child-width child-height child-bw)
  1017.                 (preferred-size child)
  1018.               (setf item-width  (max item-width  (+ child-width child-bw child-bw))
  1019.                     item-height (max item-height (+ child-height child-bw child-bw)))))
  1020.           (values item-width item-height))))
  1021.     
  1022.     
  1023.     (defun column-preferred-size (column item-width item-height)
  1024.       "Return the preferred width and height for COLUMN, assuming the given
  1025.        ITEM-WIDTH and ITEM-HEIGHT."
  1026.       (with-slots (children) column
  1027.         (let ((preferred-margin 8))
  1028.           (values
  1029.             (+ item-width preferred-margin preferred-margin)
  1030.             (+ (* (length children) (+ item-height preferred-margin))
  1031.                preferred-margin)))))
  1032.  
  1033.  
  1034. If the current column size is smaller  than its preferred size, then the  column
  1035. trys to expand.  The  column requests approval  from its geometry  manager for a
  1036. larger size  by  calling  change-geometry.   The  :accept-p  argument  given  to
  1037. change-geometry is true; that is, any modification to the requested change  made
  1038. by the geometry  manager is  accepted without  any further  negotiation.  If the
  1039. current column size  is at  least as  big as  its preferred  size, then  no size
  1040. change is  necessary.   In  this  case,  the  column simply rearranges its items
  1041. within its current dimensions by calling column-adjust.
  1042.  
  1043.     
  1044.     (defun column-adjust (column &optional item-width item-height)
  1045.       "Rearrange COLUMN items according to current COLUMN size. If given, ITEM-WIDTH
  1046.        and ITEM-HEIGHT define the new size for all items."
  1047.       (with-slots (children width height) column
  1048.         (when children
  1049.           ;; Compute preferred item size, if necessary
  1050.           (unless item-height
  1051.             (multiple-value-setq (item-width item-height)
  1052.               (column-item-size column)))
  1053.           
  1054.           ;; Compute item spacing
  1055.           (let* ((number-items (length children))
  1056.                  (margin       (max (round (- width item-width)
  1057.                                            2)
  1058.                                     0))
  1059.                  (space        (max (round (- height (* number-items item-height))
  1060.                                            (1+ number-items))
  1061.                                     0)))
  1062.             
  1063.             ;; Set size and position of each child
  1064.             (let ((y 0))
  1065.               (dolist (child children)
  1066.                 (let ((bw (contact-border-width child)))
  1067.                   (with-state (child)
  1068.                     (resize child (- item-width bw bw) (- item-height bw bw) bw) 
  1069.                     (move child margin (incf y space))))
  1070.                 (incf y item-height)))))))
  1071.  
  1072. The resize  and  move  functions  are  called  to  actually relocate each child.
  1073. Notice that these calls lie within a with-state form.  with-state is a CLX macro
  1074. that makes window reconfiguration more efficient  by combining the new size  and
  1075. position into a single request to the X server.  Calling resize and move here is
  1076. very important.  Many contact classes define :after methods for these  functions
  1077. in order to implement side-effects of geometry changes.  For example, resizing a
  1078. column causes it to rearrange all of its items within the new size.  The  column
  1079. resize method is called by change-geometry above when :accept-p is true.
  1080.  
  1081.     (defmethod resize :after ((column column) width height border-width)
  1082.       (declare (ignore width height border-width))
  1083.       (column-adjust column))
  1084.  
  1085.  
  1086. The  other   geometry   management   method   defined   by  column  is  for  the
  1087. manage-geometry function.  manage-geometry is called by change-geometry in order
  1088. to grant approval for  a change requested  by a column  item.  Implementing this
  1089. method is often  complicated.  A  column has  several factors  to consider  when
  1090. approving a change to an item.  First, no position change can be approved if  it
  1091. differs from the position already determined by column policy.  Also, an item is
  1092. not allowed to shrink because it must maintain the same (maximum) size preferred
  1093. by all other items.  (A more complex policy implementation might allow items  to
  1094. shrink, resetting all items to any new maximum size.) On the other hand, if  the
  1095. requested item change  does not  affect its  overall width  and height, then the
  1096. change can be approved immediately.
  1097.     
  1098.     (defmethod manage-geometry ((column column) child x y width height border-width &key)
  1099.       (with-slots
  1100.         ((child-width width)
  1101.          (child-height height)
  1102.          (child-border-width border-width)
  1103.          (child-x x)
  1104.          (child-y y))
  1105.         child
  1106.     
  1107.         (let*
  1108.           ;; No position change can be approved.
  1109.           ((position-approved-p     (not (or (unless (null x) (/= x child-x))
  1110.                                              (unless (null y) (/= y child-y)))))
  1111.            
  1112.            ;; Check if requested size change can be approved.
  1113.            (total-width            (+ child-width child-border-width child-border-width))
  1114.            (total-height           (+ child-height child-border-width child-border-width))
  1115.            (requested-width        (or width child-width))
  1116.            (requested-height       (or height child-height))
  1117.            (requested-border-width (or border-width child-border-width))
  1118.            (new-total-width        (+ requested-width requested-border-width requested-border-width))
  1119.            (new-total-height       (+ requested-height requested-border-width requested-border-width)))
  1120.     
  1121.           ;; Refuse size change immediately if it reduces item size
  1122.           (when (or (< new-total-width total-width) (< new-total-height total-height))
  1123.             (return-from manage-geometry
  1124.               nil
  1125.               child-x
  1126.               child-y
  1127.               (- child-width requested-border-width requested-border-width)
  1128.               (- child-height requested-border-width requested-border-width)                 
  1129.               requested-border-width))
  1130.     
  1131.           ;; Approve size change immediately if it does not affect item size
  1132.           (when (and (= new-total-width total-width) (= new-total-height total-height))     
  1133.             (return-from manage-geometry
  1134.               position-approved-p 
  1135.               child-x
  1136.               child-y
  1137.               requested-width
  1138.               requested-height
  1139.               requested-border-width))
  1140.     
  1141.           ;; Otherwise, a larger item size has been requested.
  1142.           ;; Check if column size can be enlarged sufficiently.
  1143.           (multiple-value-bind (column-width column-height)
  1144.               (column-preferred-size column new-total-width new-total-height)
  1145.     
  1146.             ;; Request change to preferred column size
  1147.             (multiple-value-bind
  1148.               (approved-p approved-x approved-y approved-width approved-height)
  1149.                 (change-geometry column :width column-width :height column-height)
  1150.               (declare (ignore approved-x approved-y))
  1151.              
  1152.               (when approved-p
  1153.                 
  1154.                 ;; Larger column size approved.
  1155.                 ;; When requested child geometry approved, change column layout to reflect new
  1156.                 ;; item size(s). Change child size here first before recomputing item layout.
  1157.                 (when position-approved-p         
  1158.                   (with-state (child)
  1159.                     (resize child requested-width requested-height requested-border-width))
  1160.                   (change-geometry column :width column-width :height column-height :accept-p t))
  1161.                 
  1162.                 (return-from manage-geometry
  1163.                   position-approved-p 
  1164.                   child-x
  1165.                   child-y
  1166.                   requested-width
  1167.                   requested-height
  1168.                   requested-border-width))
  1169.               
  1170.               ;; Larger column size NOT approved. Return best item size that could fit
  1171.               ;; approved column size
  1172.               (return-from manage-geometry
  1173.                 nil
  1174.                 child-x
  1175.                 child-y
  1176.                 (- approved-width requested-border-width requested-border-width)
  1177.                 (- (floor approved-height (length (composite-children column)))
  1178.                    requested-border-width requested-border-width)
  1179.                 requested-border-width))))))
  1180.     
  1181.  
  1182. What if an item asks to become bigger?  In this case, the column itself may need
  1183. to expand, so the  manage-geometry method for  column begins a  negotiation with
  1184. the column's geometry manager.  A call to changed-geometry returns the  approval
  1185. from the column geometry manager.  If approved, the column grows to fit the  new
  1186. item  size  and  returns  its  own  approval  for  the  changed   item geometry.
  1187. Otherwise,  the  column  is  forced  to   refuse  the  larger  item  size,   but
  1188. manage-geometry returns the best item size possible for the column size granted.
  1189.  
  1190. 5. Using Resources
  1191.  
  1192. This section  shows  how  the  resource  database  can  be  used  to  modify the
  1193. appearance and  behavior  of  an  application  user  interface, without actually
  1194. modifying.  the application program.  Resources  offer users a way  to fine-tune
  1195. an application user interface in  ways that application programmers  and contact
  1196. programmers may not have anticipated.
  1197.  
  1198. Resources allows  user  and  programmers  to  cooperate  in  defining  the  user
  1199. interface.   Contact  programmers  declare  which  contact  attributes  may   be
  1200. specified as  resources,  by  giving  the  appropriate  resource declarations to
  1201. defcontact.  Application programmers, who instantiate contacts, can also control
  1202. user access to  resource values.   The application  program can  either set  the
  1203. resource value initarg to make-contact, or it can leave it to be defaulted  from
  1204. the resource database.   Finally, users  can invoke  define-resources to provide
  1205. default resource values which suit their preferences.
  1206.  
  1207. In order to demonstrate  how resources are  used, the resource-menu  function is
  1208. defined.  Similar to  the just-say-lisp  function, resource-menu  creates a menu
  1209. containing the  given  items,  displays  the  menu  at the pointer position, and
  1210. returns  the   user's   choice.    resource-menu   uses  the  :name  argument to
  1211. make-contact to assign resource names to the menu and each item.  The  :defaults
  1212. argument to  make-contact  is  also  used  to  pass  along  application-specific
  1213. resource defaults.
  1214.  
  1215.  
  1216.     (defun resource-menu (host menu-name item-defaults &rest buttons)
  1217.       (let*
  1218.         ((display (open-contact-display 'resource-menu :host host))         
  1219.          (menu    (make-contact 'menu :parent display :name menu-name)))    
  1220.         
  1221.         ;; Create menu items
  1222.         (dolist (label buttons)
  1223.           (make-contact 'button
  1224.                         :parent   (menu-manager menu)
  1225.                         :name     (intern (string label))
  1226.                         :label    (format nil "~:(~a~)" label)
  1227.                         :defaults item-defaults))    
  1228.         
  1229.         ;; Set menu callback to return chosen item label
  1230.         (add-callback menu :select 'throw-menu-selection menu)
  1231.         
  1232.         ;; Display the menu so that first item is at x,y
  1233.         (initialize-geometry menu)
  1234.         (multiple-value-bind (x y) (query-pointer (contact-parent menu))
  1235.           (menu-present menu x y))
  1236.         
  1237.         ;; Event processing loop
  1238.         (let ((selected (catch :menu-selection
  1239.                           (loop (process-next-event display)))))
  1240.           
  1241.           ;; Close server connection
  1242.           (close-display display)
  1243.           
  1244.           ;; Return selected string
  1245.           selected)))
  1246.  
  1247.  
  1248. 5.1 Defining Resources
  1249.  
  1250. The  beatlemenuia   function   contains   several   examples   of   the  use  of
  1251. define-resources.  beatlemenuia itself is simply  a wrapper which allows  one to
  1252. set the value  of the  X server  host and  to experiment  with different sets of
  1253. resource defaults for menu items.
  1254.  
  1255.     (defun beatlemenuia (host &optional defaults)
  1256.       ;; ... examples of define-resources ...
  1257.       )
  1258.     
  1259.     
  1260. Inside beatlemenuia, define-resources  is called  to store  resource bindings in
  1261. the resource database.
  1262.  
  1263.     ;;;----------------------------------------------------------------------------+
  1264.     ;;;                                                                            |
  1265.     ;;;                                 Example 1                                  |
  1266.     ;;;                                                                            |
  1267.     ;;;----------------------------------------------------------------------------+
  1268.  
  1269.     (define-resources
  1270.       (* beatles title) "Who is your favorite Beatle?")
  1271.  
  1272. In Example 1, define-resources  binds the resource  name (* beatles  title) to a
  1273. specific value --- the string "Who is your favorite Beatle?" --- and stores this
  1274. resource binding  in  the  resource  database  given  by  the  special  variable
  1275. *database*.  The meaning of this resource  binding is that title string  for the
  1276. contact named beatles should be "Who is your favorite Beatle?" and that  beatles
  1277. may appear anywhere in the contact hierarchy.  Now, if an application creates  a
  1278. menu named beatles without specifying its title, then this default title  string
  1279. will be read from *database* and used.
  1280.  
  1281. By default, CLUE establishes a top-level  binding for *database*, binding it  to
  1282. an empty  resource-database  object.   The  basic  functions  for  creating  and
  1283. manipulating a  resource-database  are  defined  by  CLX.   An  application  may
  1284. manipulate several resource databases, in  which case the binding  of *database*
  1285. needs to be carefully  controlled by both  the program and  its users when using
  1286. resources.
  1287.  
  1288. Example 2  defines  resource  bindings  for  button  foreground, background, and
  1289. border resources.  Note that a resource name may include the name of a class  of
  1290. contacts, as well as  the name of  a specific contact  instance.  In Example  2,
  1291. resource-menu displays a  menu in  which the  buttons are  black rectangles with
  1292. white labels.  As a result of Example 1, the title of this menu is "Who is  your
  1293. favorite Beatle?".
  1294.  
  1295.     ;;;----------------------------------------------------------------------------+
  1296.     ;;;                                                                            |
  1297.     ;;;                                 Example 2                                  |
  1298.     ;;;                                                                            |
  1299.     ;;;----------------------------------------------------------------------------+
  1300.     
  1301.         (format t "~%Buttons are white-on-black ...")
  1302.         
  1303.         (define-resources (* button foreground) :white
  1304.                           (* button background) :black
  1305.                           (* button border)     :white)
  1306.         
  1307.         (format t " Choice is ~a"
  1308.                 (resource-menu host 'Beatles defaults 'John 'Paul 'George 'Ringo))
  1309.         
  1310.         (undefine-resources (* button foreground) :white
  1311.                             (* button background) :black
  1312.                             (* button border) :white)
  1313.         (unless (y-or-n-p "~%Continue?") (return))
  1314.  
  1315. After  resource-menu  returns,  undefine-resources  is  called  to  remove   the
  1316. previously-stored  resource   bindings   from   *database*.    For  convenience,
  1317. undefine-resources has the same argument list as define-resources; however, only
  1318. the resource names are significant, and the resource values are ignored.
  1319.  
  1320. Example 3 demonstrates the  use of the  display resource name  and the power  of
  1321. "wild-card" matching for  resource names.   The resource  name (resource-menu  *
  1322. font) matches the font resource for  any contact belonging to the  display named
  1323. resource-menu.  As a result, the menu title and button labels all appear in  the
  1324. given font.  Note that the font value is specified as a font name  string.  When
  1325. a contact is created and its font resource value is read from the database, then
  1326. the convert function automatically converts the  given name string into an  open
  1327. font object.
  1328.     
  1329.     ;;;----------------------------------------------------------------------------+
  1330.     ;;;                                                                            |
  1331.     ;;;                                 Example 3                                  |
  1332.     ;;;                                                                            |
  1333.     ;;;----------------------------------------------------------------------------+
  1334.     
  1335.         (format t "~%Use font FG-22 everywhere ...")
  1336.         
  1337.         (define-resources (resource-menu * font) "fg-22")
  1338.         
  1339.         (format t " Choice is ~a"
  1340.                 (resource-menu host 'Beatles defaults 'John 'Paul 'George 'Ringo))
  1341.         
  1342.         (undefine-resources (resource-menu * font) "fg-22")
  1343.         (unless (y-or-n-p "~%Continue?") (return))    
  1344.     
  1345.     
  1346. Example 4 shows another "wild-card" resource name used to specify the background
  1347. pattern for  all  components  of  the  contact  named  beatles.   The pattern is
  1348. specified as a gray-scale  value between 0.0  and 1.0.  CLUE  defines a built-in
  1349. convert method that converts the value 0.8 into a two-color background pixmap in
  1350. which approximately 80% of the pixels are 1 and 20% are 0.
  1351.  
  1352.     ;;;----------------------------------------------------------------------------+
  1353.     ;;;                                                                            |
  1354.     ;;;                                 Example 4                                  |
  1355.     ;;;                                                                            |
  1356.     ;;;----------------------------------------------------------------------------+
  1357.     
  1358.         (format t "~%Use gray background in menu ...")
  1359.         
  1360.         (define-resources (* beatles * background) 0.8)
  1361.         
  1362.         (format t " Choice is ~a"
  1363.                 (resource-menu host 'Beatles defaults 'John 'Paul 'George 'Ringo))
  1364.         
  1365.         (undefine-resources (* beatles * background) 0.8)
  1366.         (unless (y-or-n-p "~%Continue?") (return))
  1367.  
  1368.     
  1369. In Example 5, resource bindings are given for specific menu items.  The resource
  1370. names therefore contain contact name symbols such as 'John and 'Ringo.  Only the
  1371. matching menu  items  are  affected.   Note  also  that a background resource is
  1372. specified with the name of  a bitmap image predefined  by CLUE.  CLUE defines  a
  1373. built-in convert method to convert image names into a corresponding pixmap.
  1374.  
  1375.     
  1376.     ;;;----------------------------------------------------------------------------+
  1377.     ;;;                                                                            |
  1378.     ;;;                                 Example 5                                  |
  1379.     ;;;                                                                            |
  1380.     ;;;----------------------------------------------------------------------------+
  1381.     
  1382.         (format t "~%Only John uses font FG-22, Ringo uses gray background ...")
  1383.         
  1384.         (define-resources (* John font)        "fg-22"
  1385.                           (* Ringo background) "50%gray")
  1386.     
  1387.         (format t " Choice is ~a"
  1388.                 (resource-menu host 'Beatles defaults 'John 'Paul 'George 'Ringo))
  1389.     
  1390.         (undefine-resources (* John font)        "fg-22"
  1391.                             (* Ringo background) "50%gray")    
  1392.         (unless (y-or-n-p "~%Continue?") (return))
  1393.     
  1394.     
  1395. In Example 6,  resources are  used to  modify not  the visual  appearance of the
  1396. button but rather  its interactive  behavior.  The  basic contact class declares
  1397. the  event-translations   slot   as   a   resource.   Therefore,  instance event
  1398. translations for any contact  can be specified  in the resource  database.  Menu
  1399. item buttons  created  in  Example  6  can  be selected only by pressing pointer
  1400. :button-3 (by default, the right pointer button).  :button-press events from the
  1401. other two  pointer  buttons  are  translated  to  the  ignore-action.  This is a
  1402. generic action function defined by  CLUE; the primary ignore-action  method uses
  1403. the CLX bell function to "beep" the X server display.
  1404.  
  1405.     ;;;----------------------------------------------------------------------------+
  1406.     ;;;                                                                            |
  1407.     ;;;                                 Example 6                                  |
  1408.     ;;;                                                                            |
  1409.     ;;;----------------------------------------------------------------------------+
  1410.     
  1411.         (format t "~%Select only with :button-3 ...")
  1412.         
  1413.         (define-resources (* button event-translations)
  1414.                           '(((:button-press :button-3) button-select)
  1415.                             ((:button-press :button-1) ignore-action)
  1416.                             ((:button-press :button-2) ignore-action)))
  1417.         
  1418.         (format t " Choice is ~a"
  1419.                 (resource-menu host 'Beatles defaults 'John 'Paul 'George 'Ringo))
  1420.         
  1421.         (undefine-resources (* button event-translations)
  1422.                           '(((:button-press :button-3) button-select)
  1423.                             ((:button-press :button-1) ignore-action)
  1424.                             ((:button-press :button-2) ignore-action)))
  1425.         (unless (y-or-n-p "~%Continue?") (return))
  1426.  
  1427.  
  1428.  
  1429. 5.2 Application Resource Defaults
  1430.  
  1431. In describing the define-resources examples found in the beatlemenuia  function,
  1432. we have assumed that the optional defaults argument was omitted.  However,  this
  1433. argument can  be  used  to  specify  application-specific defaults for menu item
  1434. resources.   Notice  that   the  value   of  defaults   is  eventually   used in
  1435. resource-menu as  the  :defaults  argument  to  make-contact  when creating item
  1436. buttons.
  1437.  
  1438. The :defaults argument to make-contact is a property list of resource  initargs.
  1439. For example:
  1440.  
  1441.     (setf item-defaults '(:font "vrb-25" :background 0.5))
  1442.  
  1443. When a contact is created,  if a resource value  is not given in  a make-contact
  1444. initarg and no value can  be found in the  resource database, then its  value is
  1445. looked up  in  the  :defaults  list.   However,  resource  defaults found in the
  1446. resource database  override  any  value  in  the :defaults list.  Therefore, the
  1447. :defaults list is a convenient way for an application to accept user preferences
  1448. in the resource database but also to supply application-specific defaults if  no
  1449. user preferences are found.
  1450.  
  1451. For example, if  beatlemenuia is  invoked with  different application  defaults,
  1452. then menus displayed may look different.
  1453.  
  1454.     (setf item-defaults '(:font "vrb-25" :background 0.5))
  1455.     (beatlemenuia 'lm item-defaults)
  1456.  
  1457. This example causes the menu item labels to appear in the font named "vrb-25" in
  1458. Example  2.   However,  in  Example  3,  this  application-specific  default  is
  1459. overridden by the user's font choice in the resource database.  Similarly, items
  1460. have a gray background in Example 3, but not in Example 2.